package com.mdp.workflow.re.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.mdp.core.service.BaseService;
import com.mdp.workflow.re.entity.ProcdefNodeInfo;
import com.mdp.workflow.re.mapper.ProcdefNodeInfoMapper;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.RepositoryService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.function.Predicate;

/**
 * 父类已经支持增删改查操作,因此,即使本类什么也不写,也已经可以满足一般的增删改查操作了.<br> 
 * 组织 com.mdp  顶级模块 workflow 大模块 re 小模块 <br>
 * 实体 ProcdefNodeInfo 表 mdp_re_procdef_node_info 当前主键(包括多主键): id; 
 ***/
@Service
public class ProcdefNodeInfoService extends BaseService<ProcdefNodeInfoMapper,ProcdefNodeInfo> {
	static Logger logger =LoggerFactory.getLogger(ProcdefNodeInfoService.class);


	@Autowired
	RepositoryService rs;


	@Autowired
	ProcdefNodeInfoCacheService nodeInfoCacheService;

	/**
	 * 自定义查询，支持多表关联
	 * @param page 分页条件
	 * @param ew 一定要，并且必须加@Param("ew")注解
	 * @param ext 如果xml中需要根据某些值进行特殊处理，可以通过这个进行传递，非必须，注解也可以不加
	 * @return
	 */
	public List<Map<String,Object>> selectListMapByWhere(IPage page, QueryWrapper ew, Map<String,Object> ext){
		return baseMapper.selectListMapByWhere(page,ew,ext);
	}




	public List<ProcdefNodeInfo> getProcdefNodeInfosFromCache(String procDefId, String branchId){
		List<ProcdefNodeInfo> vos= this.nodeInfoCacheService.getNodeInfos(procDefId,branchId);
		if(vos==null) {
			vos= this.getBpmnActs(procDefId,branchId);
		}else {
			return vos;
		}
		if(vos==null) {
			vos=new ArrayList<>();
			nodeInfoCacheService.putNodeInfos(procDefId, branchId,vos);
		}else {
			nodeInfoCacheService.putNodeInfos(procDefId,branchId, vos);
		}
		return vos;

	}

	public Collection<FlowElement> findAllFlowElement(String processDefinitionId){
		Collection<FlowElement> flowElements=rs.getBpmnModel(processDefinitionId).getMainProcess().getFlowElements();
		return flowElements;
	}


	public List<ProcdefNodeInfo> getBpmnActs(String procDefId,String branchId) {
		Collection<FlowElement> flowElements=findAllFlowElement(procDefId);
		List<ProcdefNodeInfo> procdefNodeInfoVoList=new ArrayList<ProcdefNodeInfo>();
		ProcdefNodeInfo p=new ProcdefNodeInfo();
		p.setProcDefId(procDefId);
		p.setBranchId(branchId);
		List<ProcdefNodeInfo> mdpProcdefNodeInfos=this.selectListByWhere(p);
 		Map<String,ProcdefNodeInfo> macts=new HashMap<>();
		for (Iterator iterator = mdpProcdefNodeInfos.iterator(); iterator.hasNext();) {
			ProcdefNodeInfo nodeInfo = (ProcdefNodeInfo) iterator.next();
			ProcdefNodeInfo nodeInfoVo=new ProcdefNodeInfo();
			BeanUtils.copyProperties(nodeInfo, nodeInfoVo);
			macts.put(nodeInfoVo.getActId(), nodeInfoVo);
		}
		for (Iterator iterator = flowElements.iterator(); iterator.hasNext();) {
			FlowElement flowElement = (FlowElement) iterator.next();
			if(flowElement instanceof UserTask){
				UserTask ut=(UserTask)flowElement;

				if(macts.containsKey(ut.getId())){
					ProcdefNodeInfo a2=macts.get(ut.getId());
					a2.setActName(ut.getName());
					a2.setProcDefId(procDefId);
					procdefNodeInfoVoList.add(a2);
				}else {
					ProcdefNodeInfo a2=new ProcdefNodeInfo();
					a2.setId(this.createKey("id"));
					a2.setActId(ut.getId());
					a2.setActName(ut.getName());
					a2.setProcDefId(procDefId);
					a2.setIsMultiple(ut.hasMultiInstanceLoopCharacteristics()?"1":"0");
					a2.setShowNextAssignees("0");
					a2.setBranchId(branchId);
					List<String> hxDeptids=ut.getCandidateGroups();
					List<String> users=ut.getCandidateUsers();
					String modelAssignee=ut.getAssignee();
					if(StringUtils.hasText(modelAssignee) && modelAssignee.contains("${INITIATOR}")) {
						a2.setAllowOverUser("0");
						a2.setToCreater("1");
					}
					if(hxDeptids!=null && hxDeptids.size()>0) {
						a2.setCandidate("1");
 					}
					if(users!=null && users.size()>0) {
						a2.setCandidate("1");
					}
					procdefNodeInfoVoList.add(a2);
				}
			}
		}
		return procdefNodeInfoVoList;
	}

	/**
	 * 新增项目时，同时新增项目团队及小组组员等
	 * @param procDefId
	 * @param procdefNodeInfoVoList
	 */
	public void addNodeInfos(String procDefId,String branchId,List<ProcdefNodeInfo> procdefNodeInfoVoList) {
		List<ProcdefNodeInfo> nodes=new ArrayList<>();
 		for (ProcdefNodeInfo procdefNodeInfoVo : procdefNodeInfoVoList) {
			procdefNodeInfoVo.setAddTime(new Date());
			procdefNodeInfoVo.setLastUpdateTime(new Date());
			ProcdefNodeInfo node=new ProcdefNodeInfo();
			procdefNodeInfoVo.setId(this.createKey("id"));
			BeanUtils.copyProperties(procdefNodeInfoVo, node);
			nodes.add(node);
		}
		if(nodes.size()>0) {
			this.batchInsert(nodes);
		}
		this.nodeInfoCacheService.putNodeInfos(procDefId, branchId,null);
	}
	//更新项目团队
	public List<ProcdefNodeInfo> updateNodeInfos(String procDefId,String branchId,List<ProcdefNodeInfo> procdefNodeInfoVoList) {
		ProcdefNodeInfo node = new ProcdefNodeInfo();
		node.setProcDefId(procDefId);
		node.setBranchId(branchId);
		List<ProcdefNodeInfo> nodeListDb = this.selectListByWhere(node);
		List<ProcdefNodeInfo> delNodeInfos=new ArrayList<>();

		//查出需要删除的组进行删除组及关联组成员
		nodeListDb.forEach(g->{

			if(procdefNodeInfoVoList.stream().noneMatch(new Predicate<ProcdefNodeInfo>() {

				@Override
				public boolean test(ProcdefNodeInfo t) {

					return t.getActId().equals(g.getActId());
				}
			})) {
				delNodeInfos.add(g);
			}

		});
		if(delNodeInfos.size()>0) {
			delNodeInfos.forEach(g->{
 				ProcdefNodeInfo delNodeInfo=new ProcdefNodeInfo();
				delNodeInfo.setId(g.getId());
				this.deleteByPk(delNodeInfo);
				//xmRecordService.addXmNodeInfoRecord(procDefId, g.getId(),"项目-团队-删除小组", "删除小组["+g.getNodeInfoName()+"]",g.getId(),null);
			});

		}

		//查询新增的组进行增加并添加组成员,同时查出修改的组进行更新组及成员
		List<ProcdefNodeInfo> procdefNodeInfoVoAdd=new ArrayList<>();
		List<ProcdefNodeInfo> procdefNodeInfoVoEdit=new ArrayList<>();
		procdefNodeInfoVoList.forEach(vo->{
			if(nodeListDb.stream().noneMatch(new Predicate<ProcdefNodeInfo>() {

				@Override
				public boolean test(ProcdefNodeInfo t) {

					return t.getActId().equals(vo.getActId());
				}
			})) {

				vo.setAddTime(new Date());
				vo.setLastUpdateTime(new Date());
				procdefNodeInfoVoAdd.add(vo);
			}else {
				vo.setLastUpdateTime(new Date());
				procdefNodeInfoVoEdit.add(vo);
			}
		});
		if(procdefNodeInfoVoAdd.size()>0) {
			procdefNodeInfoVoAdd.forEach(gvo -> {
				gvo.setId(this.createKey("id"));
				gvo.setProcDefId(procDefId);
				ProcdefNodeInfo g = new ProcdefNodeInfo();
				BeanUtils.copyProperties(gvo,g);
				g.setBranchId(branchId);
				this.insert(g);
			});
		}
		if(procdefNodeInfoVoEdit.size()>0) {
 			procdefNodeInfoVoEdit.forEach(gvo -> {
				gvo.setProcDefId(procDefId);
				ProcdefNodeInfo g = new ProcdefNodeInfo();
				BeanUtils.copyProperties(gvo, g);
				this.updateSomeFieldByPk(g);
			});
		}

		this.nodeInfoCacheService.putNodeInfos(procDefId, branchId,null);
		return procdefNodeInfoVoList;
	}

}

